package com.unswift.utils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import com.unswift.annotation.api.Api;
import com.unswift.annotation.api.ApiField;
import com.unswift.annotation.api.ApiMethod;

@Api(value="字符串公共处理类", author="unswift", date="2023-04-08", version="1.0.0")
public class StringUtils {

	@ApiMethod(value="字符串两端去空格", params=@ApiField("字符串"), returns=@ApiField("两端去除空格后的字符串"))
	public static String trim(String source){
		if(ObjectUtils.isEmpty(source)){
			return source;
		}
		return source.trim();
	}
	
	@ApiMethod(value="字符串去除空格，包括中间的空格", params=@ApiField("字符串"), returns=@ApiField("去除空格后的字符串"))
	public static String trimAll(String source){
		if(ObjectUtils.isEmpty(source)){
			return source;
		}
		return source.replaceAll("\\s", "");
	}
	
	@ApiMethod(value="字符串转小写", params=@ApiField("字符串"), returns=@ApiField("转小写后的字符串"))
	public static String toLower(String source){
		if(ObjectUtils.isEmpty(source)){
			return source;
		}
		return source.toLowerCase();
	}
	
	@ApiMethod(value="字符串转大写", params=@ApiField("字符串"), returns=@ApiField("转大写后的字符串"))
	public static String toUpper(String source){
		if(ObjectUtils.isEmpty(source)){
			return source;
		}
		return source.toUpperCase();
	}
	
	@ApiMethod(value="首字母小写", params=@ApiField("字符串"), returns=@ApiField("首字母小写后的字符串"))
	public static String toFirstLower(String source){
		if(ObjectUtils.isEmpty(source)){
			return source;
		}
		int first=source.codePointAt(0);
		if(first>=65 && first<=90){
			first+=32;
		}
		return ((char)first)+source.substring(1);
	}
	
	@ApiMethod(value="首字母大写", params=@ApiField("字符串"), returns=@ApiField("首字母大写后的字符串"))
	public static String toFirstUpper(String source){
		if(ObjectUtils.isEmpty(source)){
			return source;
		}
		int first=source.codePointAt(0);
		if(first>=97 && first<=122){
			first-=32;
		}
		return ((char)first)+source.substring(1);
	}
	
	/**
	 * 将异常堆栈转换为String
	 * @param e
	 * @return
	 */
	@ApiMethod(value="将异常栈转换为字符串", params=@ApiField("异常对象"), returns=@ApiField("堆栈字符串形式"))
	public static String toStackTrace(Throwable e){
		PrintWriter printWriter=null;
		try {
			StringWriter stringWriter=new StringWriter();
			printWriter=new PrintWriter(stringWriter);
			e.printStackTrace(printWriter);
			return stringWriter.getBuffer().toString();
		} finally {
			FileUtils.close(printWriter);
		}
	}
	
	@ApiMethod(value="字符串是否开始于目标字符串，不区分大小写", params={@ApiField("字符串"), @ApiField("开始于字符串")}, returns=@ApiField("是否开始于，true：开始于，false：不开始于"))
	public static boolean startsWith(String source, String prefix){
		if(ObjectUtils.isEmpty(source) || ObjectUtils.isEmpty(prefix)){
			return false;
		}
		return source.toLowerCase().startsWith(prefix.toLowerCase());
	}
	
	@ApiMethod(value="字符串是否开始于目标字符串数组中的任何一个字符串，不区分大小写", params={@ApiField("字符串"), @ApiField("开始于字符串数组")}, returns=@ApiField("是否开始于数组中的其中一个，true：开始于，false：不开始于"))
	public static boolean startsWiths(String source, String[] prefixs){
		source=source.toLowerCase();
		for (String prefix : prefixs) {
			if(startsWith(source, prefix)){
				return true;
			}
		}
		return false;
	}
	
	@ApiMethod(value="字符串是否开始于目标字符串数组中的任何一个字符串，不区分大小写", params={@ApiField("字符串"), @ApiField("开始于字符串Set")}, returns=@ApiField("是否开始于Set中的其中一个，true：开始于，false：不开始于"))
	public static boolean startsWiths(String source, Set<String> prefixs){
		return startsWiths(source, prefixs.toArray(new String[prefixs.size()]));
	}
	
	@ApiMethod(value="字符串是否开始于目标字符串数组中的任何一个字符串，不区分大小写", params={@ApiField("字符串"), @ApiField("开始于字符串List")}, returns=@ApiField("是否开始于List中的其中一个，true：开始于，false：不开始于"))
	public static boolean startsWiths(String source, List<String> prefixs){
		return startsWiths(source, prefixs.toArray(new String[prefixs.size()]));
	}
	
	@ApiMethod(value="字符串是否结束于目标字符串，不区分大小写", params={@ApiField("字符串"), @ApiField("结束于字符串")}, returns=@ApiField("是否结束于，true：开始于，false：不开始于"))
	public static boolean endsWith(String source, String suffix){
		return source.toLowerCase().endsWith(suffix.toLowerCase());
	}
	
	@ApiMethod(
			value="字符串是否开始于目标字符串数组中的任何一个字符串，"
					+ "如果结束于字符串时S:开始的，则从第二个字符开始判断，如果结束于字符串是以R:开始的，则需要用正则判断，不区分大小写，否则直接判断", 
			params={@ApiField("字符串"), @ApiField("开始于字符串")}, 
			returns=@ApiField("是否开始于，true：开始于，false：不开始于"))
	public static boolean endsWiths(String source, String[] suffixs){
		source=source.replaceAll("\\s+", " ").toLowerCase();
		boolean match=false;
		for (String suffix : suffixs) {
			if(suffix.startsWith("S:") && source.endsWith(suffix.substring(2).toLowerCase())){
				return true;
			}else if(suffix.startsWith("R:") && source.matches(".*("+suffix.substring(2).toLowerCase()+")$")){
				return true;
			}else{
				match=endsWith(source, suffix);
				if(match){
					return true;
				}
			}
		}
		return false;
	}
	
	@ApiMethod(value="获取12号字体字符串所占宽度，中文字符14，英文字符8", params=@ApiField("字符串"), returns=@ApiField("宽度值"))
	public static int stringToWidth(String source){
		if(ObjectUtils.isEmpty(source)){
			return 0;
		}
		int length=source.length(), width=0;
		for (int i = 0; i < length; i++) {
			int code = source.codePointAt(i);
			if(code<255){
				width+=8;
			}else{
				width+=14;
			}
		}
		return width;
	}
	
	@ApiMethod(value="获取字符串长度，中文占2，英文占1", params=@ApiField("字符串"), returns=@ApiField("长度值"))
	public static int stringToWordLength(String source){
		if(ObjectUtils.isEmpty(source)){
			return 0;
		}
		int length=source.length(), width=0;
		for (int i = 0; i < length; i++) {
			int code = source.codePointAt(i);
			if(code<255){
				width+=1;
			}else{
				width+=2;
			}
		}
		return width;
	}
	
	@ApiMethod(value="获取指定长度字符一样的字符串", params={@ApiField("总长度"), @ApiField("字符")}, returns=@ApiField("每个字符都一样的字符串"))
	public static String sameCharacter(int size, char c){
		StringBuilder sameBuff=new StringBuilder();
		for (int i = 0; i < size; i++) {
			sameBuff.append(c);
		}
		return sameBuff.toString();
	}
	
	@ApiMethod(value="将列表按照指定字符链接起来", params={@ApiField("列表对象"), @ApiField("链接字符串，如果直接链接则设置空即可")}, returns=@ApiField("链接后的字符串"))
	public static String join(List<String> list, String join){
		if(ObjectUtils.isEmpty(list)){
			return "";
		}
		join=ObjectUtils.init(join, ",");
		StringBuilder result=new StringBuilder();
		for (String item : list) {
			result.append(item).append(join);
		}
		int length = result.length();
		result.delete(length-join.length(), length);
		return result.toString();
	}
	
	@ApiMethod(value="将数组按照指定字符链接起来", params={@ApiField("数组对象"), @ApiField("链接字符串，如果直接链接则设置空即可")}, returns=@ApiField("链接后的字符串"))
	public static String join(String[] array, String join){
		if(ObjectUtils.isEmpty(array)){
			return "";
		}
		join=ObjectUtils.init(join, ",");
		StringBuilder result=new StringBuilder();
		for (String item : array) {
			result.append(item).append(join);
		}
		int length = result.length();
		result.delete(length-join.length(), length);
		return result.toString();
	}
	
	@ApiMethod(value="将数组按照指定字符链接起来", params={@ApiField("数组对象"), @ApiField("链接字符串，如果直接链接则设置空即可")}, returns=@ApiField("链接后的字符串"))
	public static String join(Object array, String join){
		if(ObjectUtils.isEmpty(array)){
			return "";
		}
		join=ObjectUtils.init(join, ",");
		StringBuilder result=new StringBuilder();
		int length = Array.getLength(array);
		for (int i = 0; i < length; i++) {
			result.append(Array.get(array, i));
			if(i<length-1){
				result.append(join);
			}
		}
		return result.toString();
	}
	
	@ApiMethod(value="把数组对象的类类型拼接起来返回", params = {@ApiField("数组对象"), @ApiField("拼接字符")})
	public static String joinContainType(Object array, String join){
		if(ObjectUtils.isEmpty(array)){
			return "";
		}
		join=ObjectUtils.init(join, ",");
		StringBuilder result=new StringBuilder();
		int length = Array.getLength(array);
		Object item;
		for (int i = 0; i < length; i++) {
			item = Array.get(array, i);
			result.append(item);
			if(ObjectUtils.isNull(item)){
				result.append("(Null)");
			}else{
				result.append("(").append(item.getClass().getSimpleName()).append(")");
			}
			if(i<length-1){
				result.append(join);
			}
		}
		return result.toString();
	}
	
	@ApiMethod(value="异常信息处理", params=@ApiField("异常信息"), returns=@ApiField("处理后的异常消息"))
	public static String handleErrorMsg(String errMsg) {
		if (errMsg == null) {
			return "空指针异常";
		}
		return errMsg.replaceAll("[\r\n]", "");
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@ApiMethod(value="将值按照指定的路径设置到map中", params={@ApiField("Map对象"), @ApiField("路径，a.b..."), @ApiField("需要设置的值")})
	public static Object setMapPath(Map<String, Object> map, String path, Object value) {
		String[] pathArray=trimAll(path).split("\\.");
		Object temp=map, prev=map;
		int index;
		String fieldName;
		for (int i = 0, length=pathArray.length; i < length; i++) {
			index=pathArray[i].indexOf("[");
			if(index!=-1){
				fieldName=pathArray[i].substring(0, index);
				if(temp instanceof Map){
					temp=((Map<?, ?>)temp).get(fieldName);
				}else{
					temp=ClassUtils.get(temp, fieldName);
				}
				if(ObjectUtils.isNotEmpty(temp)){
					if(temp.getClass().isArray()){
						temp=Array.get(temp, Integer.parseInt(pathArray[i].substring(index+1, pathArray[i].length()-1)));
					}else if(temp instanceof List){
						temp=((List<?>)temp).get(Integer.parseInt(pathArray[i].substring(index+1, pathArray[i].length()-1)));
					}else{
						throw ExceptionUtils.message("parse.path.unknow.class.type", temp.getClass(), path);
					}
				}else{
					temp=new ArrayList();
					((Map)prev).put(fieldName, temp);
					if(i==length-1){
						((List)temp).add(value);
					}else{
						prev=temp;
						temp=new HashMap<String, Object>();
						((List)prev).add(temp);
					}
				}
			}else{
				if(temp instanceof Map){
					temp=((Map<?, ?>)temp).get(pathArray[i]);
					if(ObjectUtils.isEmpty(temp)){
						if(i==length-1){
							((Map<String, Object>)prev).put(pathArray[i], value);
						}else{
							temp=new HashMap<String, Object>();
							((Map<String, Object>)prev).put(pathArray[i], temp);
						}
					}
				}else{
					temp=ClassUtils.get(temp, pathArray[i]);
					if(ObjectUtils.isEmpty(temp)){
						if(i==length-1){
							ClassUtils.set(prev, pathArray[i], value);
						}else{
							temp=new HashMap<String, Object>();
							ClassUtils.set(prev, pathArray[i], temp);
						}
					}
				}
			}
			prev=temp;
		}
		return temp;
	}
	
	@ApiMethod(value="在Map中获取指定路径的值", params={@ApiField("Map对象"), @ApiField("路径，a.b...")}, returns=@ApiField("路径值"))
	public static Object getMapPath(Map<String, Object> map, String path) {
		String[] pathArray=trimAll(path).split("\\.");
		Object temp=map;
		int index;
		String fieldName;
		for (int i = 0, length=pathArray.length; i < length; i++) {
			index=pathArray[i].indexOf("[");
			if(index!=-1){
				fieldName=pathArray[i].substring(0, index);
				if(temp instanceof Map){
					temp=((Map<?, ?>)temp).get(fieldName);
				}else{
					temp=ClassUtils.get(temp, fieldName);
				}
				if(ObjectUtils.isNotEmpty(temp)){
					if(temp.getClass().isArray()){
						temp=Array.get(temp, Integer.parseInt(pathArray[i].substring(index+1, pathArray[i].length()-1)));
					}else if(temp instanceof List){
						temp=((List<?>)temp).get(Integer.parseInt(pathArray[i].substring(index+1, pathArray[i].length()-1)));
					}else{
						throw ExceptionUtils.message("parse.path.unknow.class.type", temp.getClass(), path);
					}
				}
			}else{
				if(temp instanceof Map){
					temp=((Map<?, ?>)temp).get(pathArray[i]);
				}else{
					temp=ClassUtils.get(temp, pathArray[i]);
				}
			}
			if(ObjectUtils.isEmpty(temp)){
				break ;
			}
		}
		return temp;
	}
	
	@ApiMethod(value="源字符串中是否包含目标字符串的项，源字符串中各项是以(itemSplit参数)隔开的，如果隔开符为null，则默认为,", params={@ApiField("源字符串"), @ApiField("目标字符串"), @ApiField("需要拆分的字符串")}, returns=@ApiField("是否包含，true：包含，false：不包含"))
	public static boolean contains(String source, String target, String itemSplit) {
		if(ObjectUtils.isEmpty(source) || ObjectUtils.isEmpty(target)){
			return false;
		}
		itemSplit=ObjectUtils.init(itemSplit, "\\,");
		String[] array=source.split(itemSplit);
		for (String item : array) {
			if(item.equals(target)){
				return true;
			}
		}
		return false;
	}
	
	@ApiMethod(value="根据词组列表及非必要下表穷举出词组的组合，如：词组a,b,c,如果下表1是不必须的，则组合情况为：ac,abc", params={@ApiField("词组列表"), @ApiField("非必要词组下表")}, returns=@ApiField("穷举的Set集合"))
	public static Set<String> exhaustionJoin(List<String> wordsList, List<Integer> notNecessary){
		return exhaustionJoin(wordsList, notNecessary, null, null);
	}
	
	@ApiMethod(
		value="根据词组列表及非必要下表穷举出词组的组合，如：词组a,b,c,如果下表1是不必须的，则组合情况为：ac,abc", 
		params={@ApiField("词组列表"), @ApiField("非必要词组下表"), @ApiField("之前的组合，递归时使用，使用者调用无需传此参数"), @ApiField("偏差，用来决定从哪个下标开始拼凑字符")}, 
		returns=@ApiField("穷举的Set集合")
	)
	private static Set<String> exhaustionJoin(List<String> wordsList, List<Integer> notNecessary, Set<String> beforeCombination, Integer deviation){
		if(notNecessary.size()==1){
			if(ObjectUtils.isEmpty(beforeCombination)){
				Set<String> combination=new HashSet<String>();
				combination.add(join(wordsList, ""));
				wordsList.remove((int)notNecessary.get(0));
				combination.add(join(wordsList, ""));
				return combination;
			}else{
				Set<String> combination=new HashSet<String>();
				List<String> wordsList2=new ArrayList<String>(wordsList);
				wordsList2.remove((int)notNecessary.get(0)-deviation);
				for (String wordGroup : beforeCombination) {
					combination.add(wordGroup+join(wordsList, ""));
					combination.add(wordGroup+join(wordsList2, ""));
				}
				return combination;
			}
		}else{
			int length=notNecessary.size();
			Set<String> combination=new HashSet<String>();
			int startIndex=0;
			for (int i = 0; i < length; i++) {
				combination=exhaustionJoin(new ArrayList<String>(wordsList.subList(startIndex, notNecessary.get(i)+1)), notNecessary.subList(i, i+1), combination, startIndex);
				startIndex=notNecessary.get(i)+1;
			}
			length=wordsList.size();
			if(startIndex<length){
				String endStr=join(wordsList.subList(startIndex, length), "");
				Set<String> combination2=new HashSet<String>(combination);
				combination=new HashSet<String>();
				for (String wordGroup : combination2) {
					combination.add(wordGroup+endStr);
				}
			}
			return combination;
		}
	}
	
	@ApiMethod(value="将实体字段转为数据库的列，驼峰变为下划线", params=@ApiField("实体字段名称"))
	public static String fieldToColumn(String fieldName){
		StringBuilder columnName=new StringBuilder();
		char item;
		for (int i = 0, length=fieldName.length(); i < length; i++) {
			item=fieldName.charAt(i);
			if(item>='A' && item<='Z'){
				columnName.append("_").append((char)(item+32));
			}else{
				columnName.append(item);
			}
		}
		return columnName.toString();
	}
	
	@ApiMethod(value="将实体字段转为数据库的列，驼峰变为下划线", params=@ApiField("实体字段名称"))
	public static String columnToField(String columnName){
		StringBuilder fieldName=new StringBuilder();
		char item;
		boolean nextUpper=false;
		for (int i = 0, length=columnName.length(); i < length; i++) {
			item=columnName.charAt(i);
			if(nextUpper){
				if(item>='a' && item<='z'){
					fieldName.append((char)(item-32));
				}else{
					fieldName.append(item);
				}
				nextUpper=false;
			}else if(item=='_'){
				nextUpper=true;
			}else{
				fieldName.append(item);
			}
		}
		return fieldName.toString();
	}
	
	@ApiMethod(value="字符串下划线转驼峰", params=@ApiField("带下划线的字符串"), returns=@ApiField("带驼峰的字符串"))
	public static String underlineToHump(String source){
		StringBuilder target=new StringBuilder();
		int length=source.length();
		boolean isUpper=false;
		for (int i = 0; i < length; i++) {
			if (source.charAt(i) == '-' || source.charAt(i) == '_') {
				isUpper=true;
			} else if(isUpper && source.codePointAt(i) >= 97 && source.codePointAt(i) <= 122){
				target.append((char)(source.codePointAt(i)-32));
				isUpper=false;
			} else {
				target.append(source.charAt(i));
			}
		}
		return target.toString();
	}
	
	@ApiMethod(value="获取UUID", returns=@ApiField("32位UUID的值"))
	public static String getUuid(){
		return UUID.randomUUID().toString().replace("-", "").toUpperCase();
	}
	
	@ApiMethod(value="将bit容量显示为可视化容量，1G=1024MB,1MB=1024KB,1KB=1024B", params=@ApiField("bit容量"), returns=@ApiField("可视化容量"))
	public static String showCapacity(long capacity){
		if(capacity<1024){
			return capacity+"B";
		}else if(capacity<1024L*1024){
			return NumberUtils.format(new BigDecimal(capacity/1024.0).setScale(3, RoundingMode.HALF_UP), "#")+"KB";
		}else if(capacity<1024L*1024*1024){
			return NumberUtils.format(new BigDecimal(capacity/1024.0/1024).setScale(3, RoundingMode.HALF_UP), "#")+"MB";
		}else{
			return NumberUtils.format(new BigDecimal(capacity/1024.0/1024/1024).setScale(3, RoundingMode.HALF_UP), "#")+"GB";
		}
	}
	
	@ApiMethod(value="执行对象的toString方法，如果为null则返回null", params = {@ApiField("执行的对象"), @ApiField("如果对象为空，则返回初始字符串")}, returns = @ApiField("执行toString方法后的结果"))
	public static String toString(Object source, String init) {
		if(ObjectUtils.isNull(source)) {
			return init;
		}
		return source.toString();
	}
	
	@ApiMethod(value="隐藏字符串中间内容，如：123456隐层后12**56", params = {@ApiField("源字符串"), @ApiField("保留前端长度"), @ApiField("保留后端长度")}, returns = @ApiField("隐藏后的字符串"))
	public static String hiddenCenter(String source, int startLength, int endLength) {
		if(ObjectUtils.isEmpty(source)) {
			return source;
		}
		int length=source.length();
		if(length<=2) {
			return source;
		}
		StringBuilder result=new StringBuilder();
		if(length<startLength+endLength) {
			int start=length%2==0?length/2-1:length/2;
			result.append(source.substring(0, start));
			if(length%2==0) {//隐层中间两个
				result.append("**").append(source.substring(start+2));
			}else {
				if(length<=5) {//隐层中间一个
					result.append("*").append(source.substring(start+1));
				}else {//隐层中间三个
					result.append("***").append(source.substring(start+3));
				}
			}
		}else {
			result.append(source.substring(0, startLength));
			int hiddenLength=length-startLength-endLength;
			for (int i = 0; i < hiddenLength; i++) {
				result.append("*");
			}
			result.append(source.substring(length-endLength));
		}
		return result.toString();
	}
	
	@ApiMethod(value="间隔隐藏字符串，如：123456隐层后1*3*5*", params = {@ApiField("源字符串")}, returns = @ApiField("隐藏后的字符串"))
	public static String hiddenInterval(String source) {
		if(ObjectUtils.isEmpty(source)) {
			return source;
		}
		int length=source.length();
		if(length<=1) {
			return source;
		}
		StringBuilder result=new StringBuilder();
		for (int i = 0; i < length; i++) {
			if(i%2==1) {
				result.append("*");
			}else {
				result.append(source.charAt(i));
			}
		}
		return result.toString();
	}
	
	@ApiMethod(value="判断一个字符串是否是英文字符+数字的组合", params = {@ApiField("需要检测的字符串")}, returns = @ApiField("是否英文字符+数字组合，true：是，false：不是"))
	public static Boolean isAsciiChar(String source, String regex) {
		if(ObjectUtils.isEmpty(source)) {
			return true;
		}
		return source.matches(String.format("^%s$", regex));
	}
	
}